﻿using Inaction.ServiceContract;
using Inaction.Rule;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;

namespace Inaction.Database
{
    [Serializable]
    public class DbProviderInfo
    {
        [System.ComponentModel.DisplayName("数据库类型")]
        public SqlType SqlType { get; set; }

        string providerName = string.Empty;
        [System.ComponentModel.DisplayName("提供器名称")]
        public string ProviderName
        {
            get { return providerName; }
            set
            {
                providerName = value;
                if (!string.IsNullOrEmpty(providerName))
                    sqlFactory = string.Empty;
            }
        }
        [System.ComponentModel.DisplayName("数据库访问组件")]
        public string File { get; set; }

        string sqlFactory = string.Empty;
        [System.ComponentModel.DisplayName("数据的Factory名称")]
        public string SqlFactory
        {
            get { return sqlFactory; }
            set
            {
                if (!string.IsNullOrEmpty(ProviderName))
                    sqlFactory = string.Empty;
                else sqlFactory = value;
            }
        }
    }
    public class DbProviderManager
    {
        #region 属性
        static string dbProviderFile = Inaction.Util.AppHelper.ConfigDirectory + "DbProvider.xml";
        static Dictionary<SqlType, DbProviderFactory> Factories = new Dictionary<SqlType, DbProviderFactory>();
        static List<DbProviderInfo> providers;
        public static List<DbProviderInfo> Providers
        {
            get
            {
                if (providers == null)
                {
                    try
                    {

                        providers = ServiceContainer.GetService<ISerializerHelper>().GetSerializer(SerializeType.Xml).DeserializeFormFile<List<DbProviderInfo>>(dbProviderFile);
                    }
                    catch
                    {
                        providers = new List<DbProviderInfo>();
                        providers.Add(
                            new DbProviderInfo
                            {
                                SqlType = SqlType.SqlServer,
                                ProviderName = "System.Data.SqlClient"
                            });
                        providers.Add(
                            new DbProviderInfo
                            {
                                SqlType = SqlType.Oracle,
                                ProviderName = "System.Data.OracleClient"
                            });
                    }
                }
                return providers;
            }
        }
        #endregion

        public static void Save()
        {
            ServiceContainer.GetService<ISerializerHelper>().GetSerializer(SerializeType.Xml).SerializeToFile(Providers, dbProviderFile);
        }
        public static System.Data.Common.DbProviderFactory GetFactory(SqlType sqlType)
        {
            if (Factories.ContainsKey(sqlType)) return Factories[sqlType];

            var provider = Providers.FirstOrDefault(p => p.SqlType == sqlType);
            if (provider == null)
            {
                new DbConfigForm().ShowDialog();
                provider = Providers.FirstOrDefault(p => p.SqlType == sqlType);
                if (provider == null) return null;
            }
            DbProviderFactory factory = null;
            if (!string.IsNullOrEmpty(provider.ProviderName))
            {
                factory = DbProviderFactories.GetFactory(provider.ProviderName);
            }
            else
            {
                var file = provider.File;
                if (!file.EndsWith(".dll"))
                {
                    file += ".dll";
                }
                var factoryType = Assembly.LoadFile(Inaction.Util.AppHelper.ExecuteDirectory + file).GetType(provider.SqlFactory);
                try
                {
                    factory = (DbProviderFactory)factoryType.GetConstructor(new Type[0]).Invoke(new object[0]);
                }
                catch (Exception)
                {
                    var fieldInfo = factoryType.GetField("Instance");
                    if (fieldInfo != null)
                    {
                        factory = (DbProviderFactory)fieldInfo.GetValue(null);
                    }
                }
            }
            Factories.Add(sqlType, factory);
            return factory;
        }
        public static string GetProviderName(SqlType sqlType)
        {
            var provider = Providers.FirstOrDefault(p => p.SqlType == sqlType);
            return provider == null ? string.Empty : (string.IsNullOrEmpty(provider.ProviderName) ? provider.SqlFactory : provider.ProviderName);
        }
    }
}
